﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Runtime.Serialization.Formatters.Binary;
using System.Data.Common;
using Peer.Security;
namespace Peer.Web
{
    /// <summary>
    /// connectionStringName为连接串名
    /// ExpireDays过期天数
    /// LoadUSPName取Cookie的存储过程
    /// SaveUSPName存Cookie的存储过程
    /// </summary>
    public class PeerCookiesProvider : CookiesProvider
    {
        /// <summary>
        /// 存在客户端未加密的Cookie名字
        /// </summary>
        const string COOKIEGUIDNAME = "PeerCookGuidName";
        /// <summary>
        /// 存在客户端加密后的Cookie名字
        /// </summary>
        private static string strCookieGuidName = PeerSecurity.EncryptString(COOKIEGUIDNAME);
        /// <summary>
        /// 默认的数据库连接名
        /// </summary>
        const string CONNECTIONNAME = "LocalSqlServer";
        const string LOADUSPNAME = "aspnet_cookie_load";
        const string SAVEUSPNAME = "aspnet_cookie_save";
        /// <summary>
        /// 默认的过期天数
        /// </summary>
        private const int COOKEIEXPIREDAYS = 100;
        private string _connectionString;
        private string _dbProviderName;
        private string _saveUSPName;
        private string _loadUSPName;
        private int _expiredays;
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);
            string connectionStringName = config["connectionStringName"] != null ? config["connectionStringName"] : CONNECTIONNAME;
            System.Configuration.ConnectionStringSettings c = System.Configuration.ConfigurationManager.ConnectionStrings[connectionStringName];
            _connectionString =c.ConnectionString;
            _dbProviderName = c.ProviderName;
            _expiredays = string.IsNullOrEmpty(config["ExpireDays"]) ? COOKEIEXPIREDAYS : Convert.ToInt32(config["ExpireDays"]);
            _loadUSPName = string.IsNullOrEmpty(config["LoadUSPName"]) ? LOADUSPNAME : config["LoadUSPName"];
            _saveUSPName = string.IsNullOrEmpty(config["SaveUSPName"]) ? SAVEUSPNAME : config["SaveUSPName"];

        }
        public override object this[string name]
        {
            get
            {
                object objReturn;
                if (CookieGuid.HasValue)
                {
                    try
                    {
                        objReturn = LoadCookie(CookieGuid.Value, name);
                    }
                    catch
                    {
                        objReturn = null;
                    }
                }
                else
                {
                    objReturn = HttpContext.Current.Session[name];
                }
                return objReturn;
            }
            set
            {
                if (HttpContext.Current.Request.Browser.Cookies)
                {
                    Guid guid;
                    if (CookieGuid.HasValue)
                    {
                        guid = CookieGuid.Value;
                    }
                    else
                    {
                        guid = Guid.NewGuid();
                        CookieGuid = guid;
                   }
                    SaveCookie(guid, name, value);
                }
                else
                {
                    HttpContext.Current.Session[name] = value;
                }
            }
        }
        private Guid? CookieGuid
        {
            set
            {
                if (value.HasValue)
                {
                    string strCookieGuidValue = PeerSecurity.EncryptString(value.Value.ToString());
                    HttpCookie newCookie = new HttpCookie(strCookieGuidName, strCookieGuidValue);
                    newCookie.Expires = DateTime.MaxValue;
                    HttpContext.Current.Response.Cookies.Add(newCookie);
                }
            }
            get
            {
                Guid? guid;
                HttpCookie obj = HttpContext.Current.Request.Cookies[strCookieGuidName];
                if (obj != null)
                {
                    try
                    {
                        string str = PeerSecurity.DecryptString(obj.Value);
                        guid = new Guid(str);
                    }
                    catch
                    {
                        guid = null;
                    }
                }
                else
                {
                    guid = null;
                }
                return guid;
            }
        }
        private byte[] Serialize(object o)
        {
            System.IO.MemoryStream stream = new System.IO.MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(stream, o);
            return stream.ToArray();
        }
        private object DeSerialize(byte[] bytes)
        {
            System.IO.MemoryStream stream = new System.IO.MemoryStream(bytes);
            BinaryFormatter bf = new BinaryFormatter();
            return bf.Deserialize(stream);
        }
        private void SaveCookie(Guid uid, string name, object obj)
        {
            DbProviderFactory dbf = DbProviderFactories.GetFactory(_dbProviderName);
            using (DbConnection con = dbf.CreateConnection())
            {
                con.ConnectionString = _connectionString;
                DbCommand cmd = con.CreateCommand();
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.CommandText = _saveUSPName;
                DbParameter par = null;

                par = cmd.CreateParameter();
                par.ParameterName = "UID";
                par.Value = uid;
                cmd.Parameters.Add(par);

                par = cmd.CreateParameter();
                par.ParameterName = "Name";
                par.Value = name;
                cmd.Parameters.Add(par);

                par = cmd.CreateParameter();
                par.ParameterName = "Value";
                par.Value = Serialize(obj);
                cmd.Parameters.Add(par);

                par = cmd.CreateParameter();
                par.ParameterName = "ExpireDays";
                par.Value = _expiredays;
                cmd.Parameters.Add(par);

                con.Open();
                cmd.ExecuteNonQuery();
                con.Close();
            }
        }
        private object LoadCookie(Guid uid, string name)
        {
            object objReturn = null;
            DbProviderFactory dbf = DbProviderFactories.GetFactory(_dbProviderName);
            using (DbConnection con = dbf.CreateConnection())
            {
                con.ConnectionString = _connectionString;
                DbCommand cmd = con.CreateCommand();
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.CommandText = _loadUSPName;
                DbParameter par = null;

                par = cmd.CreateParameter();
                par.ParameterName = "UID";
                par.Value = uid;
                cmd.Parameters.Add(par);

                par = cmd.CreateParameter();
                par.ParameterName = "Name";
                par.Value = name;
                cmd.Parameters.Add(par);

                con.Open();
                DbDataReader reader = cmd.ExecuteReader();
                if (reader.Read())
                {
                    long startIndex = 0;
                    const int bufferSize = 100;
                    int intCol = reader.GetOrdinal("Value");
                    byte[] outByte = new byte[bufferSize];
                    long retval;
                    System.IO.MemoryStream stream = new System.IO.MemoryStream();
                    System.IO.BinaryWriter bw = new System.IO.BinaryWriter(stream);
                    do
                    {
                        retval = reader.GetBytes(intCol, startIndex, outByte, 0, bufferSize);
                        bw.Write(outByte);
                        bw.Flush();
                        startIndex += bufferSize;
                    } while (retval == bufferSize);
                    objReturn = DeSerialize(stream.ToArray());
                }
                else
                {
                    objReturn = null;
                }
                con.Close();
            }
            return objReturn;
        }
    }
}
